// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "entd/crypto_pkcs11.h"

#include "base/logging.h"
#include "base/string_util.h"
#include "base/string_number_conversions.h"
#include <chromeos/utility.h>
#include <openssl/bn.h>

#include "entd/utils.h"

namespace entd {

namespace crypto {

// PKCS#11 Helper Constants.
static const CK_OBJECT_CLASS kPublicKeyClass = CKO_PUBLIC_KEY;
static const CK_OBJECT_CLASS kPrivateKeyClass = CKO_PRIVATE_KEY;
static const CK_BBOOL kCkTrue = TRUE;
static const CK_BBOOL kCkFalse = FALSE;
static const CK_BYTE kPublicExponent[] = { 0x01, 0x00,
                                           0x01 }; // 65537 in bytes.
static const CK_ULONG kDefaultKeySize = 2048; // in bits.

typedef std::vector<CK_ATTRIBUTE> TemplateAttributes;
typedef std::map<CK_ATTRIBUTE_TYPE, CK_BBOOL> TemplateBoolValues;
typedef std::map<CK_ATTRIBUTE_TYPE, CK_ULONG> TemplateUlongValues;
typedef std::map<CK_ATTRIBUTE_TYPE, chromeos::Blob> TemplateBinaryValues;
typedef std::map<CK_ATTRIBUTE_TYPE, std::string> TemplateStringValues;

// PKCS#11 structures uses pointers to buffers. This structure holds the
// buffers result of translation of user's input, required to complete
// the request.
typedef struct {
  TemplateBoolValues bool_values;
  TemplateUlongValues ulong_values;
  TemplateBinaryValues binary_values;
  TemplateStringValues string_values;
} TemplateValueStore;

// Dirty little macros to make copying things from PKCS#11 structs a little
// cleaner.
#define SET_CK_STRING(self, record, field)                                  \
  self->Set(v8::String::New(#field),                                        \
            v8::String::New(reinterpret_cast<char*>(&record.field[0]),      \
                            CkStringLength(record.field,                    \
                                           sizeof(record.field))))

#define SET_CK_ULONG(self, record, field)                                   \
  self->Set(v8::String::New(#field),                                        \
            v8::Integer::NewFromUnsigned(record.field))

// Reflect PKCS#11 constants into JavaScript.
#define SET_CK_CONST(self, name)                                            \
  self->Set(v8::String::New(#name),                                         \
            v8::Integer::NewFromUnsigned(name), v8::ReadOnly)

bool CKRVToString(CK_RV rv, std::string* rv_string) {
  v8::Handle<v8::Function> ctor = Pkcs11::constructor_template()->GetFunction();

  v8::Handle<v8::Array> ary = ctor->GetPropertyNames();
  for (uint32_t i = 0; i < ary->Length(); ++i) {
    v8::String::AsciiValue name(ary->Get(i));
    if (strncmp("CKR_", *name, 4) == 0) {
      uint32_t value = ctor->Get(ary->Get(i))->Uint32Value();
      if (value == rv) {
        rv_string->assign(*name, name.length());
        return true;
      }
    }
  }

  unsigned int urv = rv;
  rv_string->assign(StringPrintf("0x%08X", urv));

  return false;
}

// Check that a CK_RV value is CKR_OK, throw a V8 exception if it is not.
bool OkOrThrow(CK_RV rv) {
  if (rv != CKR_OK) {
    std::string rv_string;
    CKRVToString(rv, &rv_string);
    utils::ThrowV8Exception("PKCS#11 Error: " + rv_string);
    return false;
  }

  return true;
}

// Check that a CK_RV value is CKR_OK, log a warning if it is not.
bool OkOrWarn(CK_RV rv) {
  if (rv != CKR_OK) {
    std::string rv_string;
    CKRVToString(rv, &rv_string);
    LOG(WARNING) << "PKCS#11 Error: " << rv_string;
    return false;
  }

  return true;
}

// Most strings returned by the PKCS#11 API are padded with spaces to a
// known length.  This function returns the length of a string minus the
// trailing space.  Some fields seem to ignore the spec and null pad rather
// than space pad, so we deal with those here too.
int CkStringLength(CK_CHAR_PTR value, CK_ULONG length) {
  for (CK_ULONG i = length; i > 0; --i) {
    if (value[i - 1] != ' ' && value[i - 1] != '\0')
      return i;
  }

  return 0;
}

bool Pkcs11::Slots::Initialize() {
  return true;
}

// static.
bool Pkcs11::Slots::InitializeTemplate(
    v8::Handle<v8::FunctionTemplate> ctor_t) {
  v8::Handle<v8::ObjectTemplate> instance_t = ctor_t->InstanceTemplate();

  instance_t->SetAccessor(v8::String::New("length"),
                          GetLength,
                          0,  // Length is readonly, so setter is NULL.
                          v8::Handle<v8::Value>(),  // Don't need any data.
                          v8::DEFAULT,  // DEFAULT AccessControl.
                          v8::DontDelete);

  instance_t->SetIndexedPropertyHandler(GetIndexedProperty,
                                        SetIndexedProperty,
                                        QueryIndexedProperty,
                                        DeleteIndexedProperty,
                                        EnumerateIndexedProperties);

  return true;
}

// static.
v8::Handle<v8::Value> Pkcs11::Slots::GetIndexedProperty(
    uint32_t index, const v8::AccessorInfo& info) {
  Pkcs11::Slots* slots = Pkcs11::Slots::UnwrapOrThrow(info.This(), "this");
  if (!slots)
    return v8::Undefined();

  CK_ULONG num_slots = 0;
  CK_RV rv = C_GetSlotList(FALSE, NULL, &num_slots);
  if (!OkOrThrow(rv))
    return v8::Undefined();

  if (index >= num_slots)
    return v8::Undefined();

  SlotMap::iterator it = slots->slot_map_.find(index);
  if (it == slots->slot_map_.end()) {
    // We haven't created a Pkcs11::Slot for this slot id yet, time to make one.
    Pkcs11::Slot::Reference slot(Pkcs11::Slot::New());
    if (slot.IsEmpty() || !slot->Initialize(index))
      return ThrowException("Error initializing slot");

    slots->slot_map_[index] = slot;
    return slot->js_object();
  }

  return it->second->js_object();
}

// static.
v8::Handle<v8::Value> Pkcs11::Slots::SetIndexedProperty(
    uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info) {
  Pkcs11::Slots* slots = Pkcs11::Slots::UnwrapOrThrow(info.This(), "this");
  if (!slots)
    return v8::Undefined();

  return ThrowException("Attempt to write to read only property");
}

// static.
v8::Handle<v8::Boolean> Pkcs11::Slots::QueryIndexedProperty(
    uint32_t index, const v8::AccessorInfo& info) {
  Pkcs11::Slots* slots = Pkcs11::Slots::UnwrapOrWarn(info.This(), "this");
  if (!slots)
    return v8::False();

  CK_ULONG num_slots = 0;
  CK_RV rv = C_GetSlotList(FALSE, NULL, &num_slots);
  if (!OkOrWarn(rv))
    return v8::False();

  if (index >= num_slots)
    return v8::False();

  return v8::True();
}

// static.
v8::Handle<v8::Boolean> Pkcs11::Slots::DeleteIndexedProperty(
    uint32_t index, const v8::AccessorInfo& info) {
  Pkcs11::Slots* slots = Pkcs11::Slots::UnwrapOrThrow(info.This(), "this");
  if (!slots)
    return v8::False();

  ThrowException("Attempt to delete read only property");
  return v8::False();
}

// static.
v8::Handle<v8::Array> Pkcs11::Slots::EnumerateIndexedProperties(
    const v8::AccessorInfo& info) {
  Pkcs11::Slots* slots = Pkcs11::Slots::UnwrapOrWarn(info.This(), "this");
  if (!slots)
    return v8::Array::New();

  CK_ULONG num_slots = 0;
  CK_RV rv = C_GetSlotList(FALSE, NULL, &num_slots);
  if (!OkOrThrow(rv))
    return v8::Array::New();

  v8::Handle<v8::Array> ary = v8::Array::New(num_slots);

  for (uint32_t i = 0; i < num_slots; ++i)
    ary->Set(i, v8::Integer::New(i));

  return ary;
}

// static.
v8::Handle<v8::Value> Pkcs11::Slots::GetLength(v8::Local<v8::String> property,
                                               const v8::AccessorInfo& info) {
  Pkcs11::Slots* slots = Pkcs11::Slots::UnwrapOrThrow(info.This(), "this");
  if (!slots)
    return v8::Undefined();

  CK_ULONG num_slots = 0;
  CK_RV rv = C_GetSlotList(FALSE, NULL, &num_slots);
  if (!OkOrThrow(rv))
    return v8::Undefined();

  return v8::Integer::NewFromUnsigned(num_slots);
}

bool Pkcs11::Slot::Initialize(CK_SLOT_ID slot_id) {
  slot_id_ = slot_id;
  return Refresh();
}

// static.
bool Pkcs11::Slot::InitializeTemplate(v8::Handle<v8::FunctionTemplate> ctor_t) {
  v8::Handle<v8::ObjectTemplate> instance_t = ctor_t->InstanceTemplate();

  SET_CK_CONST(ctor_t, CKF_TOKEN_PRESENT);
  SET_CK_CONST(ctor_t, CKF_REMOVABLE_DEVICE);
  SET_CK_CONST(ctor_t, CKF_HW_SLOT);

  BindMethod(instance_t, &Pkcs11::Slot::CallRefresh, "refresh");
  instance_t->SetAccessor(v8::String::New("token"),
                          GetToken,
                          0,  // Token is readonly, so setter is NULL.
                          v8::Handle<v8::Value>(),  // Don't need any data.
                          v8::DEFAULT,  // DEFAULT AccessControl.
                          v8::DontDelete);

  return true;
}

v8::Handle<v8::Value> Pkcs11::Slot::CallRefresh(const v8::Arguments& args) {
  if (!Refresh())
    return ThrowException("Unexpected error");

  return v8::Undefined();
}

bool Pkcs11::Slot::Refresh() {
  CK_SLOT_INFO slot_info;
  CK_RV rv = C_GetSlotInfo(slot_id_, &slot_info);
  if (!OkOrWarn(rv))
    return false;

  v8::Handle<v8::Object> self = js_object();
  if (self.IsEmpty())
    return false;

  SET_CK_STRING(self, slot_info, slotDescription);
  SET_CK_STRING(self, slot_info, manufacturerID);

  SET_CK_ULONG(self, slot_info, flags);

  if (slot_info.flags & CKF_TOKEN_PRESENT) {
    if (token_.IsEmpty()) {
      if (!token_.Copy(Pkcs11::Token::New()) || !token_->Initialize(slot_id_)) {
        LOG(ERROR) << "Error creating Pkcs11::Token instance";
        return false;
      }
    }
  } else {
    token_.set_native_ptr(NULL);
  }

  return true;
}

// static.
v8::Handle<v8::Value> Pkcs11::Slot::GetToken(v8::Local<v8::String> property,
                                             const v8::AccessorInfo& info) {
  Pkcs11::Slot* slot = Pkcs11::Slot::UnwrapOrThrow(info.This(), "this");
  if (!slot)
    return v8::Undefined();

  if (slot->token_.IsEmpty())
    return v8::Null();

  return slot->token_.js_object();
}

bool Pkcs11::Token::Initialize(CK_SLOT_ID slot_id) {
  slot_id_ = slot_id;

  js_object()->Set(v8::String::NewSymbol("slotId"),
                   v8::Integer::NewFromUnsigned(slot_id_));

  return Refresh();
}

// static.
bool Pkcs11::Token::InitializeTemplate(
    v8::Handle<v8::FunctionTemplate> ctor_t) {

  // Opencryptoki defaults, as per their README: http://tinyurl.com/2cafmsp.
  ctor_t->Set(v8::String::New("DEFAULT_SO_PIN"),
              v8::String::New("87654321"));
  ctor_t->Set(v8::String::New("DEFAULT_USER_PIN"),
              v8::String::New("12345678"));

  // openSession() flags...
  SET_CK_CONST(ctor_t, CKF_RW_SESSION);

  // token flags...
  SET_CK_CONST(ctor_t, CKF_RNG);
  SET_CK_CONST(ctor_t, CKF_WRITE_PROTECTED);
  SET_CK_CONST(ctor_t, CKF_LOGIN_REQUIRED);
  SET_CK_CONST(ctor_t, CKF_USER_PIN_INITIALIZED);
  SET_CK_CONST(ctor_t, CKF_RESTORE_KEY_NOT_NEEDED);
  SET_CK_CONST(ctor_t, CKF_CLOCK_ON_TOKEN);
  SET_CK_CONST(ctor_t, CKF_PROTECTED_AUTHENTICATION_PATH);
  SET_CK_CONST(ctor_t, CKF_DUAL_CRYPTO_OPERATIONS);
  SET_CK_CONST(ctor_t, CKF_TOKEN_INITIALIZED);
  SET_CK_CONST(ctor_t, CKF_USER_PIN_COUNT_LOW);
  SET_CK_CONST(ctor_t, CKF_USER_PIN_FINAL_TRY);
  SET_CK_CONST(ctor_t, CKF_USER_PIN_LOCKED);
  SET_CK_CONST(ctor_t, CKF_USER_PIN_TO_BE_CHANGED);
  SET_CK_CONST(ctor_t, CKF_SO_PIN_COUNT_LOW);
  SET_CK_CONST(ctor_t, CKF_SO_PIN_FINAL_TRY);
  SET_CK_CONST(ctor_t, CKF_SO_PIN_LOCKED);
  SET_CK_CONST(ctor_t, CKF_SO_PIN_TO_BE_CHANGED);

  v8::Handle<v8::ObjectTemplate> instance_t = ctor_t->InstanceTemplate();

  BindMethod(instance_t, &Pkcs11::Token::CallRefresh, "refresh");
  BindMethod(instance_t, &Pkcs11::Token::InitToken, "initToken");
  BindMethod(instance_t, &Pkcs11::Token::OpenSession, "openSession");
  BindMethod(instance_t, &Pkcs11::Token::CloseAllSessions, "closeAllSessions");

  return true;
}

bool Pkcs11::Token::Refresh() {
  CK_TOKEN_INFO token_info;
  CK_RV rv = C_GetTokenInfo(slot_id_, &token_info);
  if (!OkOrWarn(rv))
    return false;

  v8::Handle<v8::Object> self = js_object();
  if (self.IsEmpty())
    return false;

  SET_CK_STRING(self, token_info, label);
  SET_CK_STRING(self, token_info, manufacturerID);
  SET_CK_STRING(self, token_info, model);
  SET_CK_STRING(self, token_info, serialNumber);

  SET_CK_ULONG(self, token_info, flags);

  SET_CK_ULONG(self, token_info, ulMaxSessionCount);
  SET_CK_ULONG(self, token_info, ulSessionCount);
  SET_CK_ULONG(self, token_info, ulMaxRwSessionCount);
  SET_CK_ULONG(self, token_info, ulRwSessionCount);
  SET_CK_ULONG(self, token_info, ulMaxPinLen);
  SET_CK_ULONG(self, token_info, ulMinPinLen);
  SET_CK_ULONG(self, token_info, ulTotalPublicMemory);
  SET_CK_ULONG(self, token_info, ulFreePublicMemory);
  SET_CK_ULONG(self, token_info, ulTotalPrivateMemory);
  SET_CK_ULONG(self, token_info, ulFreePrivateMemory);

  return true;
}

v8::Handle<v8::Value> Pkcs11::Token::InitToken(const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: soPin");

  v8::String::AsciiValue ascii_pin(args[0]);

  if (args.Length() < 2)
    return ThrowException("Missing required parameter: label");

  v8::String::AsciiValue ascii_label(args[1]);
  if (ascii_label.length() > 32)
    return ThrowException("Label must be 32 characters or less");

  CK_CHAR ck_label[32];
  memset(ck_label, ' ', sizeof(ck_label));
  memcpy(ck_label, *ascii_label, ascii_label.length());

  OkOrThrow(C_InitToken(slot_id_,
                        reinterpret_cast<CK_CHAR_PTR>(*ascii_pin),
                        ascii_pin.length(), ck_label));

  return v8::Undefined();
}

v8::Handle<v8::Value> Pkcs11::Token::OpenSession(const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: sessionFlags");

  uint32_t session_flags = args[0]->Uint32Value();

  CK_SESSION_HANDLE session_handle = 0;

  if (!OkOrThrow(C_OpenSession(slot_id_, session_flags | CKF_SERIAL_SESSION,
                               NULL, NULL, &session_handle))) {
    return v8::Undefined();
  }

  Pkcs11::Session::Reference session = Pkcs11::Session::New();
  if (!session->Initialize(slot_id_, session_handle))
    return ThrowException("Unexpected error");

  return session->js_object();
}

v8::Handle<v8::Value> Pkcs11::Token::CloseAllSessions(
    const v8::Arguments& args) {
  OkOrThrow(C_CloseAllSessions(slot_id_));
  return v8::Undefined();
}

v8::Handle<v8::Value> Pkcs11::Token::CallRefresh(const v8::Arguments& args) {
  if (!Refresh())
    return ThrowException("Unexpected error");

  return v8::Undefined();
}

bool Pkcs11::Session::Initialize(const CK_SLOT_ID slot_id,
                                 const CK_SESSION_HANDLE& session_handle) {
  slot_id_ = slot_id;
  session_handle_ = session_handle;

  return Refresh();
}

// static.
bool Pkcs11::Session::InitializeTemplate(
    v8::Handle<v8::FunctionTemplate> ctor_t) {

  // Session states...
  SET_CK_CONST(ctor_t, CKU_SO);
  SET_CK_CONST(ctor_t, CKS_RO_PUBLIC_SESSION);
  SET_CK_CONST(ctor_t, CKS_RO_USER_FUNCTIONS);
  SET_CK_CONST(ctor_t, CKS_RW_PUBLIC_SESSION);
  SET_CK_CONST(ctor_t, CKS_RW_USER_FUNCTIONS);
  SET_CK_CONST(ctor_t, CKS_RW_SO_FUNCTIONS);

  // Session flags...
  SET_CK_CONST(ctor_t, CKF_RW_SESSION);
  SET_CK_CONST(ctor_t, CKF_SERIAL_SESSION);

  // User types for login()...
  SET_CK_CONST(ctor_t, CKU_SO);
  SET_CK_CONST(ctor_t, CKU_USER);

  // Keypair generation.
  SET_CK_CONST(ctor_t, CKM_RSA_PKCS_KEY_PAIR_GEN);

  v8::Handle<v8::ObjectTemplate> instance_t = ctor_t->InstanceTemplate();

  BindMethod(instance_t, &Pkcs11::Session::CallRefresh, "refresh");
  BindMethod(instance_t, &Pkcs11::Session::Close, "close");
  BindMethod(instance_t, &Pkcs11::Session::Login, "login");
  BindMethod(instance_t, &Pkcs11::Session::Logout, "logout");
  BindMethod(instance_t, &Pkcs11::Session::InitPin, "initPin");
  BindMethod(instance_t, &Pkcs11::Session::SetPin, "setPin");
  BindMethod(instance_t, &Pkcs11::Session::GenerateKeyPair, "generateKeyPair");
  BindMethod(instance_t, &Pkcs11::Session::FindObjects, "findObjects");
  BindMethod(instance_t, &Pkcs11::Session::CreateObject, "createObject");

  BindMethod(instance_t, &Pkcs11::Session::LogoutAndClose, "logoutAndClose");

  return true;
}

bool Pkcs11::Session::Refresh() {
  CK_SESSION_INFO session_info;
  CK_RV rv = C_GetSessionInfo(session_handle_, &session_info);
  if (!OkOrWarn(rv))
    return false;

  v8::Handle<v8::Object> self = js_object();
  if (self.IsEmpty())
    return false;

  SET_CK_ULONG(self, session_info, slotID);
  SET_CK_ULONG(self, session_info, state);
  SET_CK_ULONG(self, session_info, flags);

  return true;
}

v8::Handle<v8::Value> Pkcs11::Session::CallRefresh(const v8::Arguments& args) {
  if (!Refresh())
    return ThrowException("Unexpected error");

  return v8::Undefined();
}

v8::Handle<v8::Value> Pkcs11::Session::InitPin(const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: pin");

  v8::String::AsciiValue ascii_pin(args[0]);

  OkOrThrow(C_InitPIN(session_handle_,
                      reinterpret_cast<CK_CHAR_PTR>(*ascii_pin),
                      ascii_pin.length()));

  return v8::Undefined();
}

v8::Handle<v8::Value> Pkcs11::Session::Close(const v8::Arguments& args) {
  if (!session_handle_)
    return ThrowException("Not open");

  if (logged_in_) {
    if (!OkOrThrow(C_Logout(session_handle_)))
      return v8::Undefined();
    logged_in_ = false;
  }

  OkOrThrow(C_CloseSession(session_handle_));
  session_handle_ = 0;
  return v8::Undefined();
}

v8::Handle<v8::Value> Pkcs11::Session::Login(const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: userType");

  uint32_t user_type = args[0]->Uint32Value();
  if (user_type != CKU_USER && user_type != CKU_SO)
    return ThrowException("Invalid value for parameter: userType");

  if (args.Length() < 2)
    return ThrowException("Missing required parameter: pin");

  v8::String::AsciiValue ascii_pin(args[1]);

  logged_in_ = false;

  CK_RV rv = C_Login(session_handle_, user_type,
                     reinterpret_cast<CK_CHAR_PTR>(*ascii_pin),
                     ascii_pin.length());

  if (rv == CKR_PIN_INCORRECT)
    return v8::False();

  if (!OkOrThrow(rv))
    return v8::Undefined();

  logged_in_ = true;
  return v8::True();
}

v8::Handle<v8::Value> Pkcs11::Session::Logout(const v8::Arguments& args) {
  OkOrThrow(C_Logout(session_handle_));
  logged_in_ = false;
  return v8::Undefined();
}

v8::Handle<v8::Value> Pkcs11::Session::LogoutAndClose(
  const v8::Arguments& args) {
  Logout(args);
  Close(args);
  return v8::Undefined();
}

v8::Handle<v8::Value> Pkcs11::Session::SetPin(const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: oldPin");

  v8::String::AsciiValue old_pin(args[0]);

  if (args.Length() < 2)
    return ThrowException("Missing required parameter: newPin");

  v8::String::AsciiValue new_pin(args[1]);

  OkOrThrow(C_SetPIN(session_handle_,
                     reinterpret_cast<CK_CHAR_PTR>(*old_pin), old_pin.length(),
                     reinterpret_cast<CK_CHAR_PTR>(*new_pin), new_pin.length())
            );

  return v8::Undefined();
}

// Returns true if specified attribute exists in the template.
bool HasAttribute(const TemplateValueStore& store,
                  const CK_ATTRIBUTE_TYPE value) {
  if (store.bool_values.find(value) != store.bool_values.end())
    return true;
  if (store.ulong_values.find(value) != store.ulong_values.end())
    return true;
  if (store.binary_values.find(value) != store.binary_values.end())
    return true;
  if (store.string_values.find(value) != store.string_values.end())
    return true;

  return false;
}

// Retrieves a ULONG attribute from the template.
// returns true if value was found. false otherwise.
bool GetUlongAttribute(const TemplateValueStore& store,
                       const CK_ATTRIBUTE_TYPE name,
                       CK_ULONG* value) {
  TemplateUlongValues::const_iterator finder = store.ulong_values.find(name);
  if (finder != store.ulong_values.end()) {
    *value = finder->second;
    return true;
  }
  return false;
}

// Copy all attributes from source to destination.
void CopyAttributes(const TemplateValueStore& source,
                    TemplateValueStore* target) {
  target->bool_values.insert(source.bool_values.begin(),
                             source.bool_values.end());
  target->ulong_values.insert(source.ulong_values.begin(),
                             source.ulong_values.end());
  target->binary_values.insert(source.binary_values.begin(),
                               source.binary_values.end());
  target->string_values.insert(source.string_values.begin(),
                               source.string_values.end());
}

// Add a BOOL attribute to the template.
bool AddBoolAttribute(const uint32_t option_code, const CK_BBOOL value,
                      TemplateValueStore* store) {
  if (HasAttribute(*store, option_code)) {
    utils::ThrowV8Exception("Attribute specified more than once: " +
                            base::IntToString(option_code));
    return false;
  }

  store->bool_values[option_code] = value;
  return true;
}

// Add an ULONG attribute to the template.
bool AddUlongAttribute(const uint32_t option_code, const CK_ULONG value,
                       TemplateValueStore* store) {
  if (HasAttribute(*store, option_code)) {
    utils::ThrowV8Exception("Attribute specified more than once: " +
                            base::IntToString(option_code));
    return false;
  }

  store->ulong_values[option_code] = value;
  return true;
}

// Adds a BYTE* attribute to the template (from a binary buffer).
bool AddBinaryAttribute(const uint32_t option_code,
                        const chromeos::Blob& value,
                        TemplateValueStore* store) {
  if (HasAttribute(*store, option_code)) {
    utils::ThrowV8Exception("Attribute specified more than once: " +
                            base::IntToString(option_code));
    return false;
  }

  store->binary_values[option_code] = value;
  return true;
}

// Converts the argument based on its type:
//   type == number -- converts to hex.
//   type != number -- assume is hex.
std::string ValueToHex(const v8::Handle<v8::Value>& option_value) {
  std::string value_hex;

  if (option_value->IsNumber()) {
    // Input is numeric. Convert to hex format.
    uint32_t value = option_value->Uint32Value();
    std::stringstream value_stream;
    value_stream << std::hex << value;
    value_hex.assign(value_stream.str());
  } else {
    // Input is non-numeric. Assume hex format.
    v8::String::AsciiValue value_ascii(option_value);
    value_hex.assign(*value_ascii);
  }

  if (value_hex.length() & 1)
    value_hex.insert(0, "0");

  return value_hex;
}

// Adds a BYTE* attribute to the template (from an HEX string).
bool AddBinaryAttribute(const uint32_t option_code,
                        const v8::Handle<v8::Value>& option_value,
                        TemplateValueStore* store) {
  std::string value_hex;
  value_hex = ValueToHex(option_value);

  chromeos::Blob value_binary;
  if (!base::HexStringToBytes(value_hex, &value_binary)) {
    utils::ThrowV8Exception("Invalid binary object");
    return false;
  }

  return AddBinaryAttribute(option_code, value_binary, store);
}

// Adds a STRING attribute to the template.
bool AddStringAttribute(const uint32_t option_code,
                        const v8::Handle<v8::Value>& option_value,
                        TemplateValueStore* store) {
  if (HasAttribute(*store, option_code)) {
    utils::ThrowV8Exception("Attribute specified more than once: " +
                            base::IntToString(option_code));
    return false;
  }

  std::string value = utils::ValueAsUtf8String(option_value);
  store->string_values[option_code] = value;
  return true;
}

// Parses the template from user input parameters, converting them
// into the appropriate buffers and storing into the template store.
//
// Once all parsing is done and all attributes are added to the store,
// BuildTemplate should be called to create the PKCS#11 compatible
// input structure.
bool ParseTemplate(const std::string& template_type,
                   const v8::Handle<v8::Value>& args,
                   TemplateValueStore* store) {
  if (!args->IsArray()) {
    utils::ThrowV8Exception("Expected array for parameter: " + template_type);
    return false;
  }

  v8::Local<v8::Array> options = v8::Array::Cast(*args);
  for (unsigned int i = 0; i < options->Length(); i++) {
    v8::Local<v8::Value> option = options->Get(i);
    if (!option->IsArray()) {
      utils::ThrowV8Exception("Expected array for parameter " +
                              template_type + " [" + base::IntToString(i) +
                              "]");
      return false;
    }
    v8::Local<v8::Array> option_values = v8::Array::Cast(*option);
    if (option_values->Length() != 2) {
      utils::ThrowV8Exception("Incorrect input parameter " +
                              base::IntToString(i) +
                              ": expected format is [name, value]");
      return false;
    }

    // Parse [name, value] pair.
    v8::Local<v8::Value> cka_name = option_values->Get(0);
    v8::Local<v8::Value> cka_value = option_values->Get(1);

    if (!cka_name->IsNumber()) {
      utils::ThrowV8Exception("Incorrect input parameter " +
                              base::IntToString(i) +
                              ": name has to be a Object.CKA* constant");
      return false;
    }

    bool ret;
    uint32_t cka_attrib = cka_name->Uint32Value();

    switch (cka_attrib) {
      // BOOL values.
      case CKA_DECRYPT:
      case CKA_ENCRYPT:
      case CKA_PRIVATE:
      case CKA_SENSITIVE:
      case CKA_SIGN:
      case CKA_TOKEN:
      case CKA_UNWRAP:
      case CKA_VERIFY:
      case CKA_WRAP: {
        CK_BBOOL value = cka_value->ToBoolean()->Value();
        ret = AddBoolAttribute(cka_attrib, value, store);
        break;
      }

      // ULONG values.
      case CKA_CLASS:
      case CKA_KEY_TYPE:
      case CKA_CERTIFICATE_TYPE:
      case CKA_MODULUS_BITS: {
        CK_ULONG value = cka_value->Uint32Value();
        ret = AddUlongAttribute(cka_attrib, value, store);
        break;
      }

      // BINARY values.
      case CKA_ID:
      case CKA_COEFFICIENT:
      case CKA_EXPONENT_1:
      case CKA_EXPONENT_2:
      case CKA_PRIME_1:
      case CKA_PRIME_2:
      case CKA_PUBLIC_EXPONENT:
      case CKA_PRIVATE_EXPONENT:
      case CKA_VALUE:
      case CKA_MODULUS: {
        ret = AddBinaryAttribute(cka_attrib, cka_value, store);
        break;
      }

      // STRING values.
      case CKA_SUBJECT:
      case CKA_LABEL: {
        ret = AddStringAttribute(cka_attrib, cka_value, store);
        break;
      }

      default:
        utils::ThrowV8Exception("Incorrect input parameter " +
                                base::IntToString(i) +
                                ": name is invalid");
        ret = false;
    }

    if (!ret)
      return false;
  }

  return true;
}

// Builds a template using the skeleton defined in the TemplateValueStore.
//
// This function should be called once per pair (Store, Attribs). Best if
// all parameters are added to the store prior to calling this function,
// and call it just once.
void BuildTemplate(const TemplateValueStore& store,
                   TemplateAttributes* attribs) {
  TemplateBoolValues::const_iterator bool_item;
  for (bool_item = store.bool_values.begin();
       bool_item != store.bool_values.end(); bool_item++) {
    CK_ATTRIBUTE attrib = { bool_item->first,
                            const_cast<CK_BBOOL*>(&bool_item->second),
                            sizeof(bool_item->second) };
    attribs->push_back(attrib);
  }

  TemplateUlongValues::const_iterator ulong_item;
  for (ulong_item = store.ulong_values.begin();
       ulong_item != store.ulong_values.end(); ulong_item++) {
    CK_ATTRIBUTE attrib = { ulong_item->first,
                            const_cast<CK_ULONG*>(&ulong_item->second),
                            sizeof(ulong_item->second) };
    attribs->push_back(attrib);
  }

  TemplateBinaryValues::const_iterator binary_item;
  for (binary_item = store.binary_values.begin();
       binary_item != store.binary_values.end(); binary_item++) {
    CK_ATTRIBUTE attrib = { binary_item->first,
                            const_cast<CK_BYTE*>(&binary_item->second[0]),
                            binary_item->second.size() };
    attribs->push_back(attrib);
  }

  TemplateStringValues::const_iterator string_item;
  for (string_item = store.string_values.begin();
       string_item != store.string_values.end(); string_item++) {
    CK_ATTRIBUTE attrib = { string_item->first,
                            const_cast<char*>(string_item->second.c_str()),
                            string_item->second.length() };
    attribs->push_back(attrib);
  }
}

v8::Handle<v8::Value> Pkcs11::Session::FindObjects(const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: search template");

  // Template attributes.
  TemplateAttributes find_template;
  TemplateValueStore find_store;

  // Parse template arguments.
  if (!ParseTemplate("template", args[0], &find_store))
     return v8::Undefined();

  BuildTemplate(find_store, &find_template);

  CK_RV rv = C_FindObjectsInit(session_handle_, &find_template[0],
                               find_template.size());

  if (!OkOrThrow(rv))
    return v8::Undefined();

  CK_ULONG object_count;
  CK_OBJECT_HANDLE object_handle;
  std::vector<Object::Reference> objects_found;
  while (true) {
    rv = C_FindObjects(session_handle_, &object_handle, 1, &object_count);
    if (!OkOrThrow(rv))
      return v8::Undefined();
    if (object_count == 0)
      break;

    Object::Reference object = Object::New();
    if (object.IsEmpty() ||
        !object->Initialize(session_handle_, object_handle)) {
      return ThrowException("Error initializing result object");
    }
    objects_found.push_back(object);
  }

  rv = C_FindObjectsFinal(session_handle_);
  if (!OkOrThrow(rv))
    return v8::Undefined();

  // Construct result array.
  v8::Handle<v8::Array> result = v8::Array::New(objects_found.size());
  for (size_t i = 0; i < objects_found.size(); ++i)
    result->Set(i, objects_found[i]->js_object());

  return result;
}

void AddPublicExponent(TemplateValueStore* store) {
  if (!HasAttribute(*store, CKA_PUBLIC_EXPONENT)) {
    chromeos::Blob public_exponent;
    public_exponent.assign(kPublicExponent,
                           kPublicExponent + sizeof(kPublicExponent));
    AddBinaryAttribute(CKA_PUBLIC_EXPONENT, public_exponent,store);
  }
}

v8::Handle<v8::Value> Pkcs11::Session::CreateObject(const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: create template");

  // Template attributes.
  TemplateAttributes create_template;
  TemplateValueStore create_store;

  // Parse template arguments.
  if (!ParseTemplate("template", args[0], &create_store))
     return v8::Undefined();

  // Check for required parameters.
  if (!HasAttribute(create_store, CKA_CLASS))
    return ThrowException("Missing required parameter: Object.CKA_CLASS");

  CK_OBJECT_CLASS object_class;
  if (!GetUlongAttribute(create_store, CKA_CLASS, &object_class))
    return ThrowException("Cannot retrieve object class");
  if (object_class == CKO_PUBLIC_KEY || object_class == CKO_PRIVATE_KEY)
    AddPublicExponent(&create_store);
  BuildTemplate(create_store, &create_template);

  CK_OBJECT_HANDLE object_handle;
  CK_RV rv = C_CreateObject(session_handle_, &create_template[0],
                            create_template.size(), &object_handle);
  if (!OkOrThrow(rv))
    return v8::Undefined();

  // Construct result object.
  Object::Reference result = Object::New();
  if (result.IsEmpty() || !result->Initialize(session_handle_, object_handle))
    return ThrowException("Error initializing result object");

  return result->js_object();
}

v8::Handle<v8::Value> Pkcs11::Session::GenerateKeyPair(
    const v8::Arguments& args) {
  if (args.Length() < 1)
    return ThrowException("Missing required parameter: mechanism");
  if (!args[0]->IsNumber() && !args[0]->IsNull())
    return ThrowException("Invalid value for parameter: mechanism");

  // Default parameters.
  uint32_t mech_type = CKM_RSA_PKCS_KEY_PAIR_GEN;

  // Parse mechanism arguments.
  if (args[0]->IsNumber())
    mech_type = args[0]->Uint32Value();

  if (args.Length() < 2)
    return ThrowException("Missing required parameter: public");

  // Public key attributes.
  TemplateValueStore public_key_store;
  TemplateAttributes public_key_template;

  // Parse public template arguments.
  if (!ParseTemplate("public", args[1], &public_key_store))
     return v8::Undefined();

  if (args.Length() < 3)
    return ThrowException("Missing required parameter: private");

  // Private key attributes.
  TemplateValueStore private_key_store;
  TemplateAttributes private_key_template;

  // Parse private template arguments.
  if (!ParseTemplate("private", args[2], &private_key_store))
     return v8::Undefined();

  // Optional common attributes.
  if (args.Length() >= 4) {
    // Common attributes.
    TemplateAttributes common_key_template;
    TemplateValueStore common_key_store;

    // Parse common template arguments.
    if (!ParseTemplate("common", args[3], &common_key_store))
       return v8::Undefined();

    // Add common values to private and public templates.
    CopyAttributes(common_key_store, &public_key_store);
    CopyAttributes(common_key_store, &private_key_store);
  }

  // Add default template values.
  if (!HasAttribute(public_key_store, CKA_CLASS))
    AddUlongAttribute(CKA_CLASS, kPublicKeyClass, &public_key_store);
  AddPublicExponent(&public_key_store);
  if (!HasAttribute(public_key_store, CKA_MODULUS_BITS))
    AddUlongAttribute(CKA_MODULUS_BITS, kDefaultKeySize, &public_key_store);
  if (!HasAttribute(private_key_store, CKA_CLASS))
    AddUlongAttribute(CKA_CLASS, kPrivateKeyClass, &private_key_store);

  // Build key templates.
  BuildTemplate(public_key_store, &public_key_template);
  BuildTemplate(private_key_store, &private_key_template);

  // Define key generation mechanism attributes.
  CK_MECHANISM key_mechanism = { mech_type, NULL_PTR, 0 };

  CK_OBJECT_HANDLE public_key;
  CK_OBJECT_HANDLE private_key;

  CK_RV rv = C_GenerateKeyPair(session_handle_, &key_mechanism,
                               &public_key_template[0],
                               public_key_template.size(),
                               &private_key_template[0],
                               private_key_template.size(),
                               &public_key, &private_key);

  if (!OkOrThrow(rv))
    return v8::Undefined();

  return v8::True();
}

bool Pkcs11::Object::Initialize(const CK_SESSION_HANDLE& session_handle,
                                const CK_OBJECT_HANDLE& object_handle) {
  session_handle_ = session_handle;
  object_handle_ = object_handle;
  return true;
}

// static
bool Pkcs11::Object::InitializeTemplate(
    v8::Handle<v8::FunctionTemplate> ctor_t) {
  // Attributes.
  SET_CK_CONST(ctor_t, CKA_CLASS);
  SET_CK_CONST(ctor_t, CKA_CERTIFICATE_TYPE);
  SET_CK_CONST(ctor_t, CKA_COEFFICIENT);
  SET_CK_CONST(ctor_t, CKA_DECRYPT);
  SET_CK_CONST(ctor_t, CKA_ENCRYPT);
  SET_CK_CONST(ctor_t, CKA_EXPONENT_1);
  SET_CK_CONST(ctor_t, CKA_EXPONENT_2);
  SET_CK_CONST(ctor_t, CKA_ID);
  SET_CK_CONST(ctor_t, CKA_KEY_TYPE);
  SET_CK_CONST(ctor_t, CKA_LABEL);
  SET_CK_CONST(ctor_t, CKA_MODULUS);
  SET_CK_CONST(ctor_t, CKA_MODULUS_BITS);
  SET_CK_CONST(ctor_t, CKA_PRIME_1);
  SET_CK_CONST(ctor_t, CKA_PRIME_2);
  SET_CK_CONST(ctor_t, CKA_PRIVATE);
  SET_CK_CONST(ctor_t, CKA_PRIVATE_EXPONENT);
  SET_CK_CONST(ctor_t, CKA_PUBLIC_EXPONENT);
  SET_CK_CONST(ctor_t, CKA_SENSITIVE);
  SET_CK_CONST(ctor_t, CKA_SIGN);
  SET_CK_CONST(ctor_t, CKA_SUBJECT);
  SET_CK_CONST(ctor_t, CKA_TOKEN);
  SET_CK_CONST(ctor_t, CKA_UNWRAP);
  SET_CK_CONST(ctor_t, CKA_VALUE);
  SET_CK_CONST(ctor_t, CKA_VERIFY);
  SET_CK_CONST(ctor_t, CKA_WRAP);

  // Key types.
  SET_CK_CONST(ctor_t, CKK_AES);
  SET_CK_CONST(ctor_t, CKK_BATON);
  SET_CK_CONST(ctor_t, CKK_CAST);
  SET_CK_CONST(ctor_t, CKK_CAST128);
  SET_CK_CONST(ctor_t, CKK_CAST3);
  SET_CK_CONST(ctor_t, CKK_CAST5);
  SET_CK_CONST(ctor_t, CKK_CDMF);
  SET_CK_CONST(ctor_t, CKK_DES);
  SET_CK_CONST(ctor_t, CKK_DES2);
  SET_CK_CONST(ctor_t, CKK_DES3);
  SET_CK_CONST(ctor_t, CKK_DH);
  SET_CK_CONST(ctor_t, CKK_DSA);
  SET_CK_CONST(ctor_t, CKK_EC);
  SET_CK_CONST(ctor_t, CKK_ECDSA);
  SET_CK_CONST(ctor_t, CKK_GENERIC_SECRET);
  SET_CK_CONST(ctor_t, CKK_IDEA);
  SET_CK_CONST(ctor_t, CKK_JUNIPER);
  SET_CK_CONST(ctor_t, CKK_KEA);
  SET_CK_CONST(ctor_t, CKK_RC2);
  SET_CK_CONST(ctor_t, CKK_RC4);
  SET_CK_CONST(ctor_t, CKK_RC5);
  SET_CK_CONST(ctor_t, CKK_RSA);
  SET_CK_CONST(ctor_t, CKK_SKIPJACK);
  SET_CK_CONST(ctor_t, CKK_VENDOR_DEFINED);
  SET_CK_CONST(ctor_t, CKK_X9_42_DH);

  // Object types.
  SET_CK_CONST(ctor_t, CKO_CERTIFICATE);
  SET_CK_CONST(ctor_t, CKO_DATA);
  SET_CK_CONST(ctor_t, CKO_DOMAIN_PARAMETERS);
  SET_CK_CONST(ctor_t, CKO_HW_FEATURE);
  SET_CK_CONST(ctor_t, CKO_PRIVATE_KEY);
  SET_CK_CONST(ctor_t, CKO_PUBLIC_KEY);
  SET_CK_CONST(ctor_t, CKO_SECRET_KEY);
  SET_CK_CONST(ctor_t, CKO_VENDOR_DEFINED);

  // Certificate types.
  SET_CK_CONST(ctor_t, CKC_X_509);
  SET_CK_CONST(ctor_t, CKC_VENDOR_DEFINED);

  v8::Handle<v8::ObjectTemplate> instance_t = ctor_t->InstanceTemplate();

  BindMethod(instance_t, &Pkcs11::Object::DestroyObject, "destroy");

  return true;
}

v8::Handle<v8::Value> Pkcs11::Object::DestroyObject(const v8::Arguments& args) {
  CK_RV rv = C_DestroyObject(session_handle_, object_handle_);
  if (!OkOrThrow(rv))
    return v8::Undefined();

  return v8::True();
}

v8::Handle<v8::Value> Pkcs11::Construct(const v8::Arguments& args) {
  if (!OkOrThrow(InitializeLibrary()))
    return v8::Undefined();

  if (!Initialize())
    return ThrowException("Error initializing Pkcs11 object");

  return args.This();
}

// static.
CK_RV Pkcs11::InitializeLibrary() {
  static CK_RV rv = CKR_CANCEL;

  if (rv != CKR_OK)
    rv = C_Initialize(NULL);

  if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED)
    rv = CKR_OK;

  OkOrWarn(rv);

  return rv;
}

bool Pkcs11::Initialize() {
  if (!OkOrWarn(InitializeLibrary()))
    return false;

  v8::Handle<v8::Object> self = js_object();

  Pkcs11::Slots::Reference slots = Pkcs11::Slots::New();
  slots->Initialize();
  self->Set(v8::String::New("slots"), slots->js_object());

  return true;
}

// static.
bool Pkcs11::InitializeTemplate(v8::Handle<v8::FunctionTemplate> ctor_t) {
  ctor_t->Set(v8::String::NewSymbol("Slots"),
              Pkcs11::Slots::constructor_template());
  ctor_t->Set(v8::String::NewSymbol("Slot"),
              Pkcs11::Slot::constructor_template());
  ctor_t->Set(v8::String::NewSymbol("Token"),
              Pkcs11::Token::constructor_template());
  ctor_t->Set(v8::String::NewSymbol("Session"),
              Pkcs11::Session::constructor_template());
  ctor_t->Set(v8::String::NewSymbol("Object"),
              Pkcs11::Object::constructor_template());

  // These are in the order they appear in opencryptoki/pkcs11types.h.
  SET_CK_CONST(ctor_t, CKR_OK);
  SET_CK_CONST(ctor_t, CKR_CANCEL);
  SET_CK_CONST(ctor_t, CKR_HOST_MEMORY);
  SET_CK_CONST(ctor_t, CKR_SLOT_ID_INVALID);
  SET_CK_CONST(ctor_t, CKR_GENERAL_ERROR);
  SET_CK_CONST(ctor_t, CKR_FUNCTION_FAILED);
  SET_CK_CONST(ctor_t, CKR_ARGUMENTS_BAD);
  SET_CK_CONST(ctor_t, CKR_NO_EVENT);
  SET_CK_CONST(ctor_t, CKR_NEED_TO_CREATE_THREADS);
  SET_CK_CONST(ctor_t, CKR_CANT_LOCK);
  SET_CK_CONST(ctor_t, CKR_ATTRIBUTE_READ_ONLY);
  SET_CK_CONST(ctor_t, CKR_ATTRIBUTE_SENSITIVE);
  SET_CK_CONST(ctor_t, CKR_ATTRIBUTE_TYPE_INVALID);
  SET_CK_CONST(ctor_t, CKR_ATTRIBUTE_VALUE_INVALID);
  SET_CK_CONST(ctor_t, CKR_DATA_INVALID);
  SET_CK_CONST(ctor_t, CKR_DATA_LEN_RANGE);
  SET_CK_CONST(ctor_t, CKR_DEVICE_ERROR);
  SET_CK_CONST(ctor_t, CKR_DEVICE_MEMORY);
  SET_CK_CONST(ctor_t, CKR_DEVICE_REMOVED);
  SET_CK_CONST(ctor_t, CKR_ENCRYPTED_DATA_INVALID);
  SET_CK_CONST(ctor_t, CKR_ENCRYPTED_DATA_LEN_RANGE);
  SET_CK_CONST(ctor_t, CKR_FUNCTION_CANCELED);
  SET_CK_CONST(ctor_t, CKR_FUNCTION_NOT_PARALLEL);
  SET_CK_CONST(ctor_t, CKR_FUNCTION_NOT_SUPPORTED);
  SET_CK_CONST(ctor_t, CKR_KEY_HANDLE_INVALID);
  SET_CK_CONST(ctor_t, CKR_KEY_SIZE_RANGE);
  SET_CK_CONST(ctor_t, CKR_KEY_TYPE_INCONSISTENT);
  SET_CK_CONST(ctor_t, CKR_KEY_NOT_NEEDED);
  SET_CK_CONST(ctor_t, CKR_KEY_CHANGED);
  SET_CK_CONST(ctor_t, CKR_KEY_NEEDED);
  SET_CK_CONST(ctor_t, CKR_KEY_INDIGESTIBLE);
  SET_CK_CONST(ctor_t, CKR_KEY_FUNCTION_NOT_PERMITTED);
  SET_CK_CONST(ctor_t, CKR_KEY_NOT_WRAPPABLE);
  SET_CK_CONST(ctor_t, CKR_KEY_UNEXTRACTABLE);
  SET_CK_CONST(ctor_t, CKR_MECHANISM_INVALID);
  SET_CK_CONST(ctor_t, CKR_MECHANISM_PARAM_INVALID);
  SET_CK_CONST(ctor_t, CKR_OBJECT_HANDLE_INVALID);
  SET_CK_CONST(ctor_t, CKR_OPERATION_ACTIVE);
  SET_CK_CONST(ctor_t, CKR_OPERATION_NOT_INITIALIZED);
  SET_CK_CONST(ctor_t, CKR_PIN_INCORRECT);
  SET_CK_CONST(ctor_t, CKR_PIN_INVALID);
  SET_CK_CONST(ctor_t, CKR_PIN_LEN_RANGE);
  SET_CK_CONST(ctor_t, CKR_PIN_EXPIRED);
  SET_CK_CONST(ctor_t, CKR_PIN_LOCKED);
  SET_CK_CONST(ctor_t, CKR_SESSION_CLOSED);
  SET_CK_CONST(ctor_t, CKR_SESSION_COUNT);
  SET_CK_CONST(ctor_t, CKR_SESSION_HANDLE_INVALID);
  SET_CK_CONST(ctor_t, CKR_SESSION_PARALLEL_NOT_SUPPORTED);
  SET_CK_CONST(ctor_t, CKR_SESSION_READ_ONLY);
  SET_CK_CONST(ctor_t, CKR_SESSION_EXISTS);
  SET_CK_CONST(ctor_t, CKR_SESSION_READ_ONLY_EXISTS);
  SET_CK_CONST(ctor_t, CKR_SESSION_READ_WRITE_SO_EXISTS);
  SET_CK_CONST(ctor_t, CKR_SIGNATURE_INVALID);
  SET_CK_CONST(ctor_t, CKR_SIGNATURE_LEN_RANGE);
  SET_CK_CONST(ctor_t, CKR_TEMPLATE_INCOMPLETE);
  SET_CK_CONST(ctor_t, CKR_TEMPLATE_INCONSISTENT);
  SET_CK_CONST(ctor_t, CKR_TOKEN_NOT_PRESENT);
  SET_CK_CONST(ctor_t, CKR_TOKEN_NOT_RECOGNIZED);
  SET_CK_CONST(ctor_t, CKR_TOKEN_WRITE_PROTECTED);
  SET_CK_CONST(ctor_t, CKR_UNWRAPPING_KEY_HANDLE_INVALID);
  SET_CK_CONST(ctor_t, CKR_UNWRAPPING_KEY_SIZE_RANGE);
  SET_CK_CONST(ctor_t, CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT);
  SET_CK_CONST(ctor_t, CKR_USER_ALREADY_LOGGED_IN);
  SET_CK_CONST(ctor_t, CKR_USER_NOT_LOGGED_IN);
  SET_CK_CONST(ctor_t, CKR_USER_PIN_NOT_INITIALIZED);
  SET_CK_CONST(ctor_t, CKR_USER_TYPE_INVALID);
  SET_CK_CONST(ctor_t, CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
  SET_CK_CONST(ctor_t, CKR_USER_TOO_MANY_TYPES);
  SET_CK_CONST(ctor_t, CKR_WRAPPED_KEY_INVALID);
  SET_CK_CONST(ctor_t, CKR_WRAPPED_KEY_LEN_RANGE);
  SET_CK_CONST(ctor_t, CKR_WRAPPING_KEY_HANDLE_INVALID);
  SET_CK_CONST(ctor_t, CKR_WRAPPING_KEY_SIZE_RANGE);
  SET_CK_CONST(ctor_t, CKR_WRAPPING_KEY_TYPE_INCONSISTENT);
  SET_CK_CONST(ctor_t, CKR_RANDOM_SEED_NOT_SUPPORTED);
  SET_CK_CONST(ctor_t, CKR_RANDOM_NO_RNG);
  SET_CK_CONST(ctor_t, CKR_DOMAIN_PARAMS_INVALID);
  SET_CK_CONST(ctor_t, CKR_BUFFER_TOO_SMALL);
  SET_CK_CONST(ctor_t, CKR_SAVED_STATE_INVALID);
  SET_CK_CONST(ctor_t, CKR_INFORMATION_SENSITIVE);
  SET_CK_CONST(ctor_t, CKR_STATE_UNSAVEABLE);
  SET_CK_CONST(ctor_t, CKR_CRYPTOKI_NOT_INITIALIZED);
  SET_CK_CONST(ctor_t, CKR_CRYPTOKI_ALREADY_INITIALIZED);
  SET_CK_CONST(ctor_t, CKR_MUTEX_BAD);
  SET_CK_CONST(ctor_t, CKR_MUTEX_NOT_LOCKED);
  SET_CK_CONST(ctor_t, CKR_VENDOR_DEFINED);

  return true;
}

}  // namespace crypto

}  // namespace entd
